//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Functions;

namespace OpenLS.Spreadsheet
{
    public static class Enumerable
    {
        public static IEnumerable<TSource> Concat<TSource> (IEnumerable<TSource> source1, IEnumerable<TSource> source2)
        {
            Contract.Assert(source1 != null);
            Contract.Assert(source2 != null);
            foreach (var source in source1)
            {
                yield return source;
            }
            foreach (var source in source2)
            {
                yield return source;
            }
        }
        public static IEnumerable<TSource> Concat<TSource>(params IEnumerable<TSource> []sources)
        {
#if DEBUG
            Contract.Assert(sources != null);
#endif
            foreach (var source in sources)
                foreach (var s in source)
            {
                yield return s;
            }
        }
        public static IEnumerable<Pair<T1, T2>> CrossProduct<T1, T2>(IEnumerable<T1> source1, IEnumerable<T2> source2)
        {
            var list2 = new List<T2>(source2);
            foreach (var t1 in source1)
            {
                foreach (var t2 in list2)
                {
                    yield return new Pair<T1, T2>(t1, t2);
                }
            }
        }
        public static int Count<T>(IEnumerable<T> source)
        {
            Contract.Requires(source != null);
            int result = 0;
            foreach (var enumerable in source)
            {
                result++;
            }
            return result;
        }
        public static int Count<T>(IEnumerable<T> source, Predicate<T> predicate)
        {
            Contract.Requires(source != null);
            int result = 0;
            foreach (var enumerable in source)
            {
                if(predicate(enumerable))
                result++;
            }
            return result;
        }
        public static IEnumerable<T> OfType<T>(IEnumerable source)
        {
            Contract.Assert(source != null);
            foreach (var tSource in source)
            {
                if (tSource is T)
                    yield return (T)tSource;
            }
        }

        public static bool Any<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            Contract.Requires(source != null);
            Contract.Requires(predicate != null);
            foreach (TSource local in source)
            {
                if (predicate(local))
                {
                    return true;
                }
            }
            return false;
        }

        public static TSource FirstOrDefault<TSource>(IEnumerable<TSource> source)
        {
            Contract.Assert(source != null);
            var list = source as IList<TSource>;
            if (list != null)
            {
                if (list.Count > 0)
                {
                    return list[0];
                }
            }
            else
            {
                using (IEnumerator<TSource> enumerator = source.GetEnumerator())
                {
                    if (enumerator.MoveNext())
                    {
                        return enumerator.Current;
                    }
                }
            }
            return default(TSource);
        }
        public static IEnumerable<TSource> Where<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)
        {
            Contract.Assert(source != null);
            Contract.Assert(predicate != null);
            foreach (var tSource in source)
            {
                if (predicate(tSource))
                    yield return tSource;
            }
        }
        public static IEnumerable<TResult> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            Contract.Assert(source != null);
            Contract.Assert(selector != null);
            return selectIterator(source, selector);
        }
        private static IEnumerable<TResult> selectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            foreach (var sourceItem in source)
            {
                yield return selector(sourceItem);
            }
        }







        public static TAccumulate Aggregate<TSource, TAccumulate>(IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
        {
            Contract.Requires(source != null);
            checkFunc(func);
            TAccumulate result = seed;
            foreach (TSource local2 in source)
            {
                result = func(result, local2);
            }
            return result;
        }

        private static void checkFunc<T1, T2, T3>(Func<T1, T2, T3> func)
        {
            if (func == null)
                throw new ArgumentNullException("func");
        }

        public static IEnumerable<TResult> SelectMany<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
        {
          //\\  Contract.Requires(source != null);
           //\\ Contract.Requires(selector != null);
            foreach (var tSource in source)
            {
                foreach (var sel in selector(tSource))
                {
                    yield return sel;
                }
            }
        }


        public static double Max(IEnumerable<double> source)
        {
            Contract.Assert(source != null);
            double d = 0.0;
            bool found = false;
            foreach (double num2 in source)
            {
                if (found)
                {
                    if ((num2 > d) || double.IsNaN(d))
                    {
                        d = num2;
                    }
                    continue;
                }
                d = num2;
                found = true;
            }
            if (!found)
            {
                throw new InvalidOperationException();
            }
            return d;
        }

        public static int Max(IEnumerable<int> source)
        {
            Contract.Assert(source != null);
            int d = 0;
            bool flag = false;
            foreach (int num2 in source)
            {
                if (flag)
                {
                    if ((num2 > d) || double.IsNaN(d))
                    {
                        d = num2;
                    }
                    continue;
                }
                d = num2;
                flag = true;
            }
            if (!flag)
            {
                throw new InvalidOperationException();
            }
            return d;
        }

        internal static double Max(IEnumerable<double> source, double defaultValue)
        {
            Contract.Requires(source != null);
            double result = 0;
            bool found = false;
            foreach (double num in source)
            {
                if (found)
                {
                    if ((num > result) || double.IsNaN(result))
                    {
                        result = num;
                    }
                    continue;
                }
                result = num;
                found = true;
            }
            if (!found)
            {
                return defaultValue;
            }
            return result;
        }

        internal static int Max(IEnumerable<int> source, int defaultValue)
        {
            Contract.Requires(source != null);
            int result = 0;
            bool found = false;
            foreach (var num in source)
            {
                if (found)
                {
                    if ((num > result) || double.IsNaN(result))
                    {
                        result = num;
                    }
                    continue;
                }
                result = num;
                found = true;
            }
            if (!found)
            {
                return defaultValue;
            }
            return result;
        }

        public static int Min<TSource>(IEnumerable<TSource> source, Func<TSource, int> selector)
        {
            return Min(Select(source, item => selector(item)));
        }
        public static double Min<TSource>(IEnumerable<TSource> source, Func<TSource, double> selector)
        {
            return Min(Select(source, item => selector(item)));
        }

        public static int Max<TSource>(IEnumerable<TSource> source, Func<TSource, int> selector)
        {
            return Max(Select(source, item => selector(item)));
        }
        public static double Max<TSource>(IEnumerable<TSource> source, Func<TSource, double> selector)
        {
            return Max(Select(source, item => selector(item)));
        }

        public static int Min(IEnumerable<int> source)
        {
            Contract.Assert(source != null);
            int result = 0;
            bool found = false;
            foreach (int num2 in source)
            {
                if (found)
                {
                    if ((num2 < result) || double.IsNaN(result))
                    {
                        result = num2;
                    }
                    continue;
                }
                result = num2;
                found = true;
            }
            if (!found)
            {
                throw new InvalidOperationException();
            }
            return result;
        }
        public static double Min(IEnumerable<double> source)
        {
            Contract.Assert(source != null);
            double result = 0;
            bool found = false;
            foreach (double num2 in source)
            {
                if (found)
                {
                    if ((num2 < result) || double.IsNaN(result))
                    {
                        result = num2;
                    }
                    continue;
                }
                result = num2;
                found = true;
            }
            if (!found)
            {
                throw new InvalidOperationException();
            }
            return result;
        }

        public class Pair<T1, T2>
        {
            public Pair()
            {

            }

            public Pair(T1 first, T2 second)
            {
                First = first;
                Second = second;
            }
            public T1 First { get; set; }
            public T2 Second { get; set; }
        }

        public static IEnumerable<Pair<T, T>> GetNeighbors<T>(IEnumerable<T> source)
        {
            T old = default(T);
            bool first = true;
            foreach (T t in source)
            {
                if (first)
                    first = false;
                else
                    yield return new Pair<T, T>(old, t);
                old = t;
            }

        }

        public static IEnumerable<TSource> Skip<TSource>(IEnumerable<TSource> source, int count)
        {
            Contract.Assert(source != null);
            int skipped = 0;
            foreach (var tSource in source)
            {
                if (skipped < count)
                    skipped++;
                else
                    yield return tSource;
            }
        }

        public static TSource FirstOrDefault<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)
        {
            Contract.Assert(source != null);
            foreach (var tSource in source)
            {
                if (predicate(tSource))
                    return tSource;
            }
            return default(TSource);
        }
        public static TSource First<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            Contract.Requires(source != null);
            Contract.Requires(predicate != null);
            Contract.Requires(Count(source) > 0);
            Contract.Requires(Any(source, predicate));
            foreach (TSource local in source)
            {
                if (predicate(local))
                {
                    return local;
                }
            }
            throw  new InvalidOperationException("no match");

        }

 

 


        public static TSource First<TSource>(IEnumerable<TSource> source)
        {
            Contract.Assert(source != null);
            var list = source as IList<TSource>;
            if (list != null)
            {
                if (list.Count > 0)
                {
                    return list[0];
                }
            }
            else
            {
                foreach (var tSource in source)
                {
                    return tSource;
                }
            }
            throw new NotSupportedException();
        }
        class Set<T>: Dictionary<T, T>
        {
            public Set()
            {
                
            }

            public Set(IEnumerable<T> items)
            {
                foreach (var item in items)
                {
                    Add(item, item);
                }
            }
        }
        internal static IEnumerable<TSource> Distinct<TSource>(IEnumerable<TSource> source)
        {
            var alreadySeen = new Set<TSource>();
            var alreadySeenDefault = false;
            foreach (var item in source)
            {
                if (item == null)
                {
                    if (alreadySeenDefault)
                        continue;
                    alreadySeenDefault = true;
                    yield return item;
                }
                else
                {
                    if (alreadySeen.ContainsKey(item))
                        continue;
                    yield return item;
                    alreadySeen.Add(item, item);
                }
            }
        }

        public static double Sum(IEnumerable<double> source)
        {
            Contract.Requires(source != null);
            double result = 0;
            foreach (var d in source)
            {
                result += d;
            }
            return result;
        }
        public static double Sum<TSource>(IEnumerable<TSource> source, Func<TSource, double> selector)
        {
            Contract.Requires(source != null);
            double result = 0;
            foreach (var tSource in source)
            {
                result += selector(tSource);
            }
            return result;
        }

        public static int Sum<TSource>(IEnumerable<TSource> source, Func<TSource, int> selector)
        {
            Contract.Requires(source != null);
            int result = 0;
            foreach (var tSource in source)
            {
                result += selector(tSource);
            }
            return result;
        }

        public static ushort Sum<TSource>(IEnumerable<TSource> source, Func<TSource, ushort> selector)
        {
            Contract.Requires(source != null);
            ushort result = 0;
            foreach (var tSource in source)
            {
                result += selector(tSource);
            }
            return result;
        }

        public static bool All<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            Contract.Requires(source != null);
            Contract.Requires(predicate != null);
            foreach (TSource local in source)
            {
                if (!predicate(local))
                {
                    return false;
                }
            }
            return true;
        }


        public static List<T> ToList<T>(IEnumerable<T> source)
        {
            Contract.Requires(source != null);
            if (source is List<T>)
                return (List<T>)source;
            return new List<T>(source);
        }
        public static T[] ToArray<T>(IEnumerable<T> source)
        {
            Contract.Requires(source != null);
            var l = ToList(source);
            return l.ToArray();
        }

        public static IEnumerable<TTarget> As<TTarget, TSource>(IEnumerable<TSource> enumerable)
            where TSource : TTarget
        {
            foreach (var source in enumerable)
            {
                yield return source;
            }
        }

        public static IEnumerable<T> Union<T>(IEnumerable<T> first, IEnumerable<T> second)
        {
           // Contract.Requires(first != null);
           // Contract.Requires(second != null);
            foreach (var e in first)
            {
                yield return e;
            }
            foreach (var e in second)
            {
                yield return e;
            }
        }

        public static IEnumerable<TSource> Reverse<TSource>(IEnumerable<TSource> dims)
        {
            List<TSource> buffer = new List<TSource>(dims);
            buffer.Reverse();
            return buffer;
        }

        public static IEnumerable<TSource> Take<TSource>(IEnumerable<TSource> source, int count)
        {
            Contract.Assert(source != null);
            Contract.Assert(count >= 0);
            int c = 0;
            foreach (var item in source)
            {
                if (c >= count)
                    break;
                yield return item;
                c++;
            }
        }

        public static Dictionary<TKey, TValue> ToDictionary<TValue, TKey>(IEnumerable<TValue> source, Func<TValue, TKey> func)
        {
            var result = new Dictionary<TKey, TValue>();
            foreach (var item in source)
            {
                result.Add(func(item), item);
            }
            return result;
        }
        public static Dictionary<TKey, TValue> ToDictionary<TSource, TValue, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector)
        {
            var result = new Dictionary<TKey, TValue>();
            foreach (var item in source)
            {
                result.Add(keySelector(item), valueSelector( item));
            }
            return result;
        }

        public static IEnumerable<TResult> Cast<TResult, TSource>(IEnumerable<TSource> source)
        {
            foreach (var item in source)
                yield return (TResult)(object)item;
        }

        public static IEnumerable<TResult> Cast<TResult>(IEnumerable source)
        {
            foreach (var item in source)
                yield return (TResult)item;
        }

        public static IEnumerable<T> Intersect<T>(IEnumerable<T> first, IEnumerable<T> second)
        {
            Dictionary<T, T> dic = new Dictionary<T, T>();
            foreach (var item in Enumerable.Distinct(first))
                dic.Add(item, item);
            foreach (var item in Distinct(second))
                if (dic.ContainsKey(item))
                    yield return item;
        }

        public static IEnumerable<T> Empty<T>()
        {
            yield break;
        }

        public static IEnumerable<int> Range(int start, int end)
        {
            for (int i = start; i <= end; i++)
                yield return i;
        }

    }
}
